/*
 * DS18B20.c
 *	功 能：DS18B20 温度传感器
 *  Created on: Dec 13, 2024
 *      Author: admire
 */

#include "DS18B20.h"
#include "Delay_uS.h"

/**********************************************************************
 * 功  能：复位DS18B20
 * 参  数：无
 **********************************************************************/
void ds18b20_reset(void)
{
    DS18B20_OUT(0);	//拉低DQ,复位
    Delay_uS(750);	//拉低750us
    DS18B20_OUT(1);	//DQ=1, 释放复位
    Delay_uS(15);	//延迟15US
}

/**********************************************************************
 * 功  能：等待DS18B20的回应
 * 参  数：无
 * 返回值：0(正常)，1(异常/不存在)
 **********************************************************************/
uint8_t ds18b20_check(void)
{
    uint8_t retry = 0;
    uint8_t rval = 0;

    while (DS18B20_IN && retry < 200)	//等待DQ变低, 等待200us
    {
        retry++;
        Delay_uS(1);
    }

    if (retry >= 200)
    {
        rval = 1;
    }
    else
    {
        retry = 0;

        while (!DS18B20_IN && retry < 240)	//等待DQ变高, 等待240us
        {
            retry++;
            Delay_uS(1);
        }

        if (retry >= 240) rval = 1;
    }

    return rval;
}

/**********************************************************************
 * 功  能：从DS18B20读取一个位
 * 参  数：无
 * 返回值：0 / 1
 **********************************************************************/
static uint8_t ds18b20_read_bit(void)
{
    uint8_t data = 0;
    DS18B20_OUT(0);
    Delay_uS(2);
    DS18B20_OUT(1);
    Delay_uS(12);

    if (DS18B20_IN)
    {
        data = 1;
    }

    Delay_uS(50);
    return data;
}

/**********************************************************************
 * 功  能：从DS18B20读取一个字节
 * 参  数：data(读到的字节数据)
 **********************************************************************/
static uint8_t ds18b20_read_byte(void)
{
    uint8_t i, b, data = 0;

    for (i = 0; i < 8; i++)
    {
        b = ds18b20_read_bit();	//DS18B20先输出低位数据 ,高位数据后输出

        data |= b << i;	//填充data的每一位
    }

    return data;
}

/**********************************************************************
 * 功  能：写一个字节到DS18B20
 * 参  数：data(要写入的字节数据)
 **********************************************************************/
static void ds18b20_write_byte(uint8_t data)
{
    uint8_t j;

    for (j = 1; j <= 8; j++)
    {
        if (data & 0x01)
        {
            DS18B20_OUT(0);	//写 1
            Delay_uS(2);
            DS18B20_OUT(1);
            Delay_uS(60);
        }
        else
        {
            DS18B20_OUT(0);	//写 0
            Delay_uS(60);
            DS18B20_OUT(1);
            Delay_uS(2);
        }

        data >>= 1;	//右移,获取高一位数据
    }
}

/**********************************************************************
 * 功  能：开始温度转换
 * 参  数：无
 **********************************************************************/
static void ds18b20_start(void)
{
    ds18b20_reset();
    ds18b20_check();
    ds18b20_write_byte(0xcc);	//skip rom
    ds18b20_write_byte(0x44);	//convert
}

/**********************************************************************
 * 功  能：初始化DS18B20 同时检测DS18B20的存在
 * 参  数：无
 * 返回值：0(正常)，1(不存在/不正常)
 **********************************************************************/
uint8_t ds18b20_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;

    DS18B20_GPIO_CLK_ENABLE();   //开启DQ引脚时钟

    gpio_init_struct.Pin = DS18B20_Pin;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_OD;	//开漏输出
    gpio_init_struct.Pull = GPIO_PULLUP;			//上拉
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;	//高速
    HAL_GPIO_Init(DS18B20_GPIO_Port, &gpio_init_struct);	//初始化DS18B20_DQ引脚
    /* DS18B20_DQ引脚模式设置,开漏输出,上拉, 这样就不用再设置IO方向了, 开漏输出的时候(=1), 也可以读取外部信号的高低电平 */

    ds18b20_reset();
    return ds18b20_check();
}

/**********************************************************************
 * 功  能：从ds18b20得到温度值(精度：0.1C)
 * 参  数：无
 * 返回值：温度值(-55~125)
 **********************************************************************/
float ds18b20_get(void)
{
    uint8_t flag = 1;	//默认温度为正数
    uint8_t TL, TH;
    short data;
    float temp;

    ds18b20_start();	//ds1820 start convert
    ds18b20_reset();
    ds18b20_check();
    ds18b20_write_byte(0xcc);   //skip rom
    ds18b20_write_byte(0xbe);   //convert
    TL = ds18b20_read_byte();   //LSB
    TH = ds18b20_read_byte();   //MSB

    if (TH > 7)
    {
    	/*
    	温度为负，查看DS18B20的温度表示法与计算机存储正负数据的原理一致：
        正数补码为寄存器存储的数据自身，负数补码为寄存器存储值按位取反后+1
        所以我们直接取它实际的负数部分，但负数的补码为取反后加一，但考虑到低位可能+1后有进位和代码冗余，
        我们这里先暂时没有作+1的处理，这里需要留意
        */
        TH = ~TH;
        TL = ~TL;
        flag = 0;	//温度为负
    }

    data = TH;	//获得高八位
    data <<= 8;
    data += TL;	//获得底八位

    /* 转换成实际温度 */
    if (flag == 0)
    {
    	/* 将温度转换成负温度，这里的+1参考前面的说明 */
    	temp = (float)(data+1) * 0.0625;
        temp = -temp;
    }
    else
    {
    	temp = (float)data * 0.0625;
    }

    return temp;
}


